package com.md.application.assembler;


import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.md.base.exception.ServerException;
import com.md.domain.model.bo.QueryLogsBO;
import com.md.domain.model.po.Logs;
import com.md.infrastructure.value.ErrorEnum;
import com.md.request.OperationLogsRequest;
import com.md.request.QueryLogsRequest;
import com.md.response.AppInfoResponse;
import com.md.response.LogsResponse;
import com.md.response.LogsTreeResponse;
import com.md.response.SimplifyLogsTreeDetailResponse;
import com.md.response.SimplifyLogsTreeResponse;
import com.md.util.ClassUtils;
import com.md.util.CollectionUtil;
import com.md.util.DateUtils;
import com.md.util.ObjectUtil;
import com.md.util.StringUtil;
import com.md.value.enums.OperationEnum;
import lombok.experimental.UtilityClass;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author 刘骄阳
 * 2022-06-12 03:13
 */
@UtilityClass
public class LogsAssembler {
  public static void checkCreateOperateLogRequest(final OperationLogsRequest request) {
    if (ObjectUtil.isAllEmpty(request.getTenantId(), request.getBrandId())) {
      throw ServerException.of(ErrorEnum.PARAM_IS_ALL_EMPTY);
    }
    if (ObjectUtil.isAllEmpty(request.getOperation(), request.getDataId(), request.getOperateUser())) {
      throw ServerException.of(ErrorEnum.PARAM_IS_ALL_EMPTY);
    }
  }

  public static List<LogsResponse> poToResponse(final List<Logs> list,
                                                final Map<Long, AppInfoResponse> appInfoResponseMap,
                                                final Map<Long, String> userNameMap) {
    ArrayList<LogsResponse> logsList = Lists.newArrayList();
    if (CollectionUtils.isEmpty(list)) {
      return logsList;
    }
    list.forEach(logs -> {
      LogsResponse logsResponse = poToResponse(logs, appInfoResponseMap.get(logs.getAppInfoId()));
      logsResponse.setOperateUser(userNameMap.get(logs.getCreateBy()));
      logsList.add(logsResponse);
    });
    return logsList;
  }

  public static QueryLogsBO buildQueryLogsRequest2BO(final QueryLogsRequest request) {
    return ClassUtils.source2Target(request, QueryLogsBO.class, t -> {
      t.setOnlyRoot(ObjectUtil.equals(request.getOnlyRoot(), true));
      t.setOperation(CollectionUtil.map(request.getOperation(), OperationEnum::getCode));
      t.setCursor(Optional.ofNullable(request.getCursor()).orElse(LocalDateTime.now()));
    });

  }

  public static LogsResponse poToResponse(final Logs logs, final AppInfoResponse appInfoResponse) {
    LogsResponse logsResponse = ClassUtils.source2Target(logs, LogsResponse.class);
    Optional.ofNullable(appInfoResponse).map(AppInfoResponse::getAppName).ifPresent(logsResponse::setModule);
    logsResponse.setOperation(OperationEnum.of(logs.getOperation()));
    logsResponse.setRequestParam(StringUtil.jsonToObject(logs.getRequestParam(), JsonNode.class));
    logsResponse.setConsumingTime(DateUtils.formatToDiff(logs.getConsumingTime()));
    return logsResponse;
  }

  public static LogsTreeResponse poToTreeResponse(final Logs logs, final AppInfoResponse appInfoResponse) {
    LogsTreeResponse logsResponse = ClassUtils.source2Target(logs, LogsTreeResponse.class);
    Optional.ofNullable(appInfoResponse).map(AppInfoResponse::getAppName).ifPresent(logsResponse::setModule);
    logsResponse.setOperation(OperationEnum.of(logs.getOperation()));
    logsResponse.setRequestParam(StringUtil.jsonToObject(logs.getRequestParam(), JsonNode.class));
    logsResponse.setConsumingTime(DateUtils.formatToDiff(logs.getConsumingTime()));
    return logsResponse;
  }

  public static SimplifyLogsTreeResponse poToSimplifyLogsTreeResponse(final List<Logs> logsList,
                                                                      final AppInfoResponse appInfoResponse,
                                                                      final Boolean needMap) {
    SimplifyLogsTreeResponse response = new SimplifyLogsTreeResponse();
    Map<String, LogsResponse> logsDataMap = Maps.newHashMap();
    Map<String, List<SimplifyLogsTreeDetailResponse>> treeMap = Maps.newHashMap();
    SimplifyLogsTreeDetailResponse rootNode = new SimplifyLogsTreeDetailResponse();

    logsList.forEach(logs -> {
      if (needMap) {
        logsDataMap.put(logs.getDataId(), poToResponse(logs, appInfoResponse));
      }
      String parentId = logs.getParentId();
      if (ObjectUtil.isEmpty(parentId)) {
        rootNode.setDataId(logs.getDataId());
        rootNode.setId(logs.getId());
      } else {
        List<SimplifyLogsTreeDetailResponse> currentList = treeMap.get(parentId);
        if (ObjectUtil.isEmpty(currentList)) {
          treeMap.put(parentId, Lists.newArrayList(SimplifyLogsTreeDetailResponse.of(logs.getId(), logs.getDataId())));
          return;
        }
        currentList.add(SimplifyLogsTreeDetailResponse.of(logs.getId(), logs.getDataId()));
      }
    });
    recursionTreeList(treeMap, rootNode);
    response.setDetailMap(logsDataMap);
    response.setTreeList(rootNode);
    return response;
  }

  private static void recursionTreeList(
    final Map<String, List<SimplifyLogsTreeDetailResponse>> treeMap,
    final SimplifyLogsTreeDetailResponse currentNode) {
    currentNode.setChildren(treeMap.get(currentNode.getDataId()));
    if (ObjectUtil.notEmpty(currentNode.getChildren())) {
      currentNode.getChildren().forEach(simplifyLogsTreeDetailResponse ->
        recursionTreeList(treeMap, simplifyLogsTreeDetailResponse));
    }
  }

  public static List<SimplifyLogsTreeDetailResponse> recursionSimplifyLogsTreeResponse(final List<Logs> logLinksList,
                                                                                       final String parentDataId) {
    List<SimplifyLogsTreeDetailResponse> children = Lists.newArrayList();
    Map<Long, String> mapFilter = CollectionUtil.toMapFilter(logLinksList, Logs::getId, Logs::getDataId, logs ->
      ObjectUtil.equals(logs.getParentId(), parentDataId));
    mapFilter.forEach((id, dataId) -> {
      SimplifyLogsTreeDetailResponse response = new SimplifyLogsTreeDetailResponse();
      children.add(response);
    });
    return children;
  }

  public static LogsTreeResponse recursionChildrenOperateLogs(final Map<String, List<Logs>> treeMap,
                                                              final Map<Long, AppInfoResponse> appInfoResponseMap,
                                                              final Logs parent) {
    LogsTreeResponse logsTreeResponse = poToTreeResponse(parent, appInfoResponseMap.get(parent.getAppInfoId()));
    logsTreeResponse.setChildren(CollectionUtil.map(treeMap.remove(parent.getDataId()),
      childrenLogs -> recursionChildrenOperateLogs(treeMap, appInfoResponseMap, childrenLogs)));
    return logsTreeResponse;
  }

  public static LogsTreeResponse recursionParentOperateLogs(final Map<String, List<Logs>> treeMap,
                                                            final Map<Long, AppInfoResponse> appInfoResponseMap,
                                                            final Logs parent) {
    LogsTreeResponse response = new LogsTreeResponse();
    List<Logs> children = treeMap.remove(parent.getDataId());
    List<LogsTreeResponse> childrenList = Lists.newArrayList();
    children.forEach(logs -> {
      LogsTreeResponse logsTreeResponse = poToTreeResponse(logs, appInfoResponseMap.get(logs.getAppInfoId()));
      logsTreeResponse.setChildren(CollectionUtil.map(treeMap.remove(logs.getDataId()),
        childrenLogs -> recursionChildrenOperateLogs(treeMap, appInfoResponseMap, childrenLogs)));
      childrenList.add(logsTreeResponse);
    });
    response.setChildren(childrenList);
    return response;
  }

}
